﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using JBeamLibrary;
using JBeamLibrary.Tools;
using NativeLibrary;

namespace DriveBeamEditor.Controls.BeamNG.Controls
{
    public partial class GameControl : VehicleControl
    {
        #region Fields

        private IntPtr _appWndHdl = IntPtr.Zero;
        private bool _busy = false;
        private const string Args = "-no3dgui -level GridMap.mis -vehicle {0}";

        public event EventHandler RequestSave;

        #endregion

        #region Properties

        public bool IsGameRunning
        {
            get { return _appWndHdl != IntPtr.Zero; }
        }

        #endregion

        #region CTORs

        public GameControl()
        {
            InitializeComponent();
        }

        #endregion

        #region Events

        #region Buttons

        private void xbStart_Click(object sender, EventArgs e)
        {
            if (LaunchGame())
            {
                xbStart.Enabled = false;
                xbClose.Enabled = true;
            }
        }

        private void xbRefreshLive_Click(object sender, EventArgs e)
        {
            if (RequestSave != null)
                RequestSave(this, new EventArgs());
        }

        private void xbClose_Click(object sender, EventArgs e)
        {
            TerminateGame();
            xbStart.Enabled = true;
            xbClose.Enabled = false;
        }

        #endregion

        #region Commands menu

        private void InputMenuItem_Click(object sender, EventArgs e)
        {
            SendInput(((ToolStripMenuItem)sender).Tag.ToStringOrEmpty());
        }

        #endregion

        #region Input menu

        private void pressESCAPEToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SendInput("{ESC}");
        }

        #endregion

        #region Game panel

        private void gamePanel_Paint(object sender, PaintEventArgs e)
        {
            RefreshGame();
        }

        private void gamePanel_Resize(object sender, EventArgs e)
        {
            RefreshGame();
        }

        private void gamePanel_SizeChanged(object sender, EventArgs e)
        {
            Invalidate();
        }

        private void gamePanel_Leave(object sender, EventArgs e)
        {

        }

        #endregion

        #endregion

        #region Methods

        #region Overrides

        public override string ToString()
        {
            return "Game control";
        }

        #endregion

        #region Game related

        /// <summary>Closes the game</summary>
        public void TerminateGame()
        {
            if (_appWndHdl == IntPtr.Zero) return;

            /* Close the game */
            User32.PostMessage(_appWndHdl, (uint)WindowsMessages.WM_CLOSE, 0, 0);
            Thread.Sleep(1000);
            _appWndHdl = IntPtr.Zero;

            /* Reset game configuration to their original state */
            ResetGameConfiguration();
        }

        /// <summary>Starts the game</summary>
        /// <returns></returns>
        public bool LaunchGame()
        {
            /* Terminate current game process */
            if (_appWndHdl != IntPtr.Zero)
                TerminateGame();

            /* Initialize game settings for running inside the editor */
            InitializeGame();

            /* Game not found */
            if (string.IsNullOrEmpty(Config.GameExeFullPath) || !File.Exists(Config.GameExeFullPath))
            {
                MessageBox.Show(this, @"Please configure the game settings in the Preferences menu",
                    @"Game file not found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }

            /* Start game process */
            var proc = Process.Start(Config.GameExeFullPath, string.Format(Args, Vehicle.Name));
            if (proc == null) return false;

            /* Get game window handle */
            while (_appWndHdl == IntPtr.Zero)
            {
                proc.WaitForInputIdle(1000);
                proc.Refresh();
                if (proc.HasExited)
                    return false;
                _appWndHdl = proc.MainWindowHandle;
            }

            /* Parent game to panel control */
            User32.SetParent(_appWndHdl, gamePanel.Handle);

            /* Remove game window border and stuff */
            User32.SetWindowLong(_appWndHdl, (int)WindowLongs.GWL_STYLE, (uint)WindowStyles.WS_VISIBLE);

            /* Update game window bounds */
            RefreshGame();

            return true;
        }

        /// <summary>Forces the game to fit to the game panel</summary>
        public void RefreshGame()
        {
            if (_appWndHdl == IntPtr.Zero) return;

            User32.MoveWindow(_appWndHdl, 0, 0, gamePanel.Width, gamePanel.Height, true);
        }

        /// <summary>Refreshes the game window and reloads the current vehicle</summary>
        public void UpdateGame()
        {
            if (_appWndHdl == IntPtr.Zero) return;

            SendInput("^r");
        }

        /// <summary>Sends an input string to the game</summary>
        /// <param name="input"></param>
        public void SendInput(string input)
        {
            /* Not started */
            if (_appWndHdl == IntPtr.Zero) return;

            /* Focus application & send input */
            _busy = true;
            User32.SetForegroundWindow(_appWndHdl);
            SendKeys.Send(input);

            /* Focus back current form */
            var form = FindForm();
            if (form == null) _busy = false;
            else User32.SetForegroundWindow(form.Handle);
            _busy = false;
        }

        /// <summary>Initializes the game to be run into the editor</summary>
        private void InitializeGame()
        {
            if (string.IsNullOrEmpty(Config.GameConfigScriptFullPath)) return;

            /* Save config script */
            var origScript = string.Format("{0}.orig", Config.GameConfigScriptFullPath);
            File.Copy(Config.GameConfigScriptFullPath, origScript, true);
            File.Delete(Config.GameConfigScriptFullPath);

            /* Configure the game for embedded mode */
            var newLines = new List<string>();
            foreach (var line in File.ReadAllLines(origScript))
            {
                if (line.StartsWith(@"$pref::Video::borderless"))
                    newLines.Add("$pref::Video::borderless = \"1\";");
                else if (line.StartsWith(@"$pref::Video::mode"))
                    newLines.Add(string.Format("$pref::Video::mode = \"{0} {1} false 32 60 4\";", 960, 540));
                else
                    newLines.Add(line);
            }
            File.AppendAllLines(Config.GameConfigScriptFullPath, newLines);
        }

        /// <summary>Resets the game configuration to its previous state</summary>
        private void ResetGameConfiguration()
        {
            if (DesignMode) return;

            /* Restore original configuration */
            var origScript = string.Format("{0}.orig", Config.GameConfigScriptFullPath);
            File.Copy(origScript, Config.GameConfigScriptFullPath, true);
            File.Delete(origScript);
        }

        #endregion

        #region VehicleControl overrides

        public override void UpdateUI(Control parent = null)
        {
            xbStart.Enabled = Vehicle != null;
        }

        protected override void OnVehicleChanged()
        {
            TerminateGame();
            base.OnVehicleChanged();
        }

        #endregion

        #endregion
    }
}
